package com.example.ddz.framework.impl;

/**
 * Created by IntelliJ IDEA.
 * User: Marcel Melzig
 * Date: 28.07.11
 * Time: 19:06
 */

import java.util.ArrayList;
import java.util.List;

import android.view.MotionEvent;
import android.view.View;

import com.example.ddz.framework.IInput.TouchEvent;
import com.example.ddz.framework.ITouchHandler;
import com.example.ddz.framework.Pooling;
import com.example.ddz.framework.Pooling.PoolObjectFactory;

/**
 * This class handles the multi touch events.
 */
public class MultiTouchHandler implements ITouchHandler
{
    private final boolean[] isTouched = new boolean[20];
    private final int[] touchX = new int[20];
    private final int[] touchY = new int[20];
    private final Pooling<TouchEvent> touchEventPool;
    private final List<TouchEvent> touchEvents = new ArrayList<TouchEvent>();
    private final List<TouchEvent> touchEventsBuffer = new ArrayList<TouchEvent>();

    /**
     * Creates an instance of the MultiTouchHandler class.
     *
     * @param view The view which fires the touch events.
     */
    public MultiTouchHandler(View view)
    {
        PoolObjectFactory<TouchEvent> factory = new PoolObjectFactory<TouchEvent>()
        {
            @Override
            public TouchEvent createObject()
            {
                return new TouchEvent();
            }
        };
        touchEventPool = new Pooling<TouchEvent>(factory, 100);
        view.setOnTouchListener(this);
    }

    /**
     * Handles the touch events and manage the pooling.
     *
     * @param v     The view which fires the events.
     * @param event The touch event.
     * @return True if the event is handled, otherwise false.
     */
    @Override
    public boolean onTouch(View v, MotionEvent event)
    {
        synchronized (this)
        {
            int action = event.getAction() & MotionEvent.ACTION_MASK;
            int pointerIndex = (event.getAction() & MotionEvent.ACTION_POINTER_ID_MASK) >> MotionEvent.ACTION_POINTER_ID_SHIFT;
            int pointerId = event.getPointerId(pointerIndex);
            TouchEvent touchEvent;
            switch (action)
            {
                case MotionEvent.ACTION_DOWN:
                case MotionEvent.ACTION_POINTER_DOWN:
                    touchEvent = touchEventPool.NewObject();
                    touchEvent.type = TouchEvent.TOUCH_DOWN;
                    touchEvent.pointer = pointerId;
                    touchEvent.x = touchX[pointerId] = (int) (event.getX(pointerIndex));
                    touchEvent.y = touchY[pointerId] = (int) (event.getY(pointerIndex));
                    isTouched[pointerId] = true;
                    touchEventsBuffer.add(touchEvent);
                    break;
                case MotionEvent.ACTION_UP:
                case MotionEvent.ACTION_POINTER_UP:
                case MotionEvent.ACTION_CANCEL:
                    touchEvent = touchEventPool.NewObject();
                    touchEvent.type = TouchEvent.TOUCH_UP;
                    touchEvent.pointer = pointerId;
                    touchEvent.x = touchX[pointerId] = (int) (event.getX(pointerIndex));
                    touchEvent.y = touchY[pointerId] = (int) (event.getY(pointerIndex));
                    isTouched[pointerId] = false;
                    touchEventsBuffer.add(touchEvent);
                    break;
                case MotionEvent.ACTION_MOVE:
                    int pointerCount = event.getPointerCount();
                    for (int i = 0; i < pointerCount; i++)
                    {
                        pointerIndex = i;
                        pointerId = event.getPointerId(pointerIndex);
                        touchEvent = touchEventPool.NewObject();
                        touchEvent.type = TouchEvent.TOUCH_DRAGGED;
                        touchEvent.pointer = pointerId;
                        touchEvent.x = touchX[pointerId] = (int) (event.getX(pointerIndex));
                        touchEvent.y = touchY[pointerId] = (int) (event.getY(pointerIndex));
                        touchEventsBuffer.add(touchEvent);
                    }
                    break;
            }
            return true;
        }
    }

    @Override
    public boolean IsTouchDown(int pointer)
    {
        synchronized (this)
        {
            return !(pointer < 0 || pointer >= 20) && isTouched[pointer];
        }
    }

    @Override
    public int GetTouchX(int pointer)
    {
        synchronized (this)
        {
            if (pointer < 0 || pointer >= 20)
                return 0;
            else
                return touchX[pointer];
        }
    }

    @Override
    public int GetTouchY(int pointer)
    {
        synchronized (this)
        {
            if (pointer < 0 || pointer >= 20)
                return 0;
            else
                return touchY[pointer];
        }
    }

    @Override
    public List<TouchEvent> GetTouchEvents()
    {
        synchronized (this)
        {
            for (TouchEvent touchEvent : touchEvents)
                touchEventPool.Free(touchEvent);

            touchEvents.clear();
            touchEvents.addAll(touchEventsBuffer);
            touchEventsBuffer.clear();
            return touchEvents;
        }
    }
}
